package zheezes.protocol.impl.wt;

import java.util.ArrayList;
import java.util.List;

import zheezes.protocol.Frame;
import zheezes.util.ArrayUtil;
import zheezes.util.ByteUtil;


public class WtFrame extends Frame {
	public static final int MAX_FRAME_LENGTH = (1 << 15);
	private static final byte AB_BIT = (1 << 6);

	protected WtConAddr rtua = new WtConAddr();
	protected WtMstAddr msta = new WtMstAddr();
	protected int fseq = 0;
	protected int iseq = 0;
	protected byte ctrl = 0x00;
	protected List<DataUnit> data = new ArrayList<DataUnit>();
	
	@Override
	public int size() {
		return sizeOfData() + 13;
	}
	
	@Override
	public int getBytes(byte[] buf, int offset) {
		int idx = offset;
		buf[idx++] = 0x68;
		idx += rtua.getBytes(buf, idx);
		byte ms = msta.getData();
		buf[idx++] = (byte) (((fseq << 6) & 0xC0) | (ms & 0x3F));
		buf[idx++] = (byte) (((iseq << 5) & 0xE0) | ((fseq >> 2) & 0x1F));
		buf[idx++] = 0x68;
		buf[idx++] = ctrl;
		ByteUtil.stoc(buf, idx, (short) sizeOfData());// L
		idx += 2;
		idx += getData(buf, idx);
		byte cs = (byte) ArrayUtil.sum(buf, offset, idx);
		buf[idx++] = cs;
		buf[idx++] = 0x16;
		
		return idx - offset;
	}
	
	@Override
	public int setBytes(byte[] buf, int offset, int max) {
		int idx = offset;
		idx++; // 0x68
		idx += rtua.setBytes(buf, idx, max);
		msta = new WtMstAddr((byte) (buf[idx] & 0x3F));
		setFseq(((buf[idx] >> 6) & 0x03) | ((buf[idx + 1] << 2) & 0x7C));
		setIseq((buf[idx + 1] >> 5) & 0x07);
		idx += 2;//
		idx++; // 0x68
		ctrl = buf[idx++]; 
		short len = ByteUtil.ctos(buf, idx);
		idx += 2;
		idx += setData(buf, idx, idx + len);
		
		return idx - offset;
	}
	
	/* TODO, to fix one request and multi-response */
	public boolean match(Frame... frames) {
		for (Frame f : frames) {
			if (!(f instanceof WtFrame) || (fseq != ((WtFrame) f).fseq)) {
				return false;
			}
		}
		return true;
	}
	
	public int sizeOfData() {
		int size = 0;
		for (DataUnit d : data) {
			size += d.size();
		}
		return size;
	}
	
	public int getData(byte[] buf, int offset) {
		int idx = offset;
		for (DataUnit d : data) {
			idx += d.getBytes(buf, idx);
		}
		return idx - offset;
	}
	
	public byte[] getData() {
		byte[] buf = new byte[sizeOfData()];
		getData(buf, 0);
		return buf;
	}
	
	public int setData(byte[] buf, int offset, int max) {
		int idx = offset;
		while(idx < max) {
			DataUnit du = DataUnit.getDataUnit(ctrl, buf, idx, max);
			if(du != null) {
				data.add(du);
				idx += du.size();
			} else {
				break;
			}
		}
		return idx - offset;
	}

	public int setData(byte[] buf) {
		return setData(buf, 0, buf.length);
	}
	
	public boolean isDeny() {
		return (ctrl & AB_BIT) != 0;
	}

	public WtConAddr getRtua() {
		return rtua;
	}

	public void setRtua(WtConAddr rtua) {
		this.rtua = rtua;
	}

	public WtMstAddr getMsta() {
		return msta;
	}

	public void setMsta(WtMstAddr msta) {
		this.msta = msta;
	}

	public int getFseq() {
		return fseq;
	}

	public void setFseq(int fseq) {
		this.fseq = fseq;
	}

	public int getIseq() {
		return iseq;
	}

	public void setIseq(int iseq) {
		this.iseq = iseq;
	}

	public byte getCtrl() {
		return ctrl;
	}

	public void setCtrl(byte ctrl) {
		this.ctrl = ctrl;
	}
	
	public int addDataUnit(DataUnit du) {
		byte c = du.getCtrl();
		if(ctrl == 0x00) {
			if(c == 0x00) {
				return -1;
			} else {
				ctrl = c;
			}
		} else {
			if(ctrl != c) {
				return -1;
			}
		}
		
		data.add(du);
		return data.size();
	}
	
	public DataUnit[] getDataUnits() {
		return data.toArray(new DataUnit[0]);
	}
	
	public static void main(String[] args) {
		String sfrm = "68 00 00 20 00 00 00 68 21 00 00 11 16";
		// String sfrm = "68 82 41 75 98 00 00 68 9E 8A 00 47 02 00 00 16 19 05 10 00 18 19 05 10 00 20 19 05 10 00 22 19 05 10 00 00 20 05 10 00 02 20 05 10 00 04 20 05 10 00 06 20 05 10 00 08 20 05 10 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FC 16";
		StringBuilder sb = new StringBuilder();
		for (char c : sfrm.toCharArray()) {
			if (Character.isDigit(c) || Character.isLetter(c)) {
				sb.append(c);
			}
		}
		byte[] buf = ByteUtil.stoa(sb.toString().toCharArray());
		// Frame frame = getFrame(buf, 0, buf.length);
		WtFrame f = new WtFrame();
		f.setBytes(buf);
		System.out.println(f.getCtrl() == (byte)0x21);
		System.out.println(f.size());
		
		int len = f.getBytes(buf, 0);
		String s = ArrayUtil.sprintf(buf, 0, len, "%02X ");
		System.out.println(s);
	}
}
